// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == 'object' && typeof module == 'object')
    // CommonJS
    mod(require('../../lib/codemirror'));
  else if (typeof define == 'function' && define.amd)
    // AMD
    define(['../../lib/codemirror'], mod);
  // Plain browser env
  else mod(CodeMirror);
})(function(CodeMirror) {
  'use strict';

  CodeMirror.defineMode('eiffel', function() {
    function wordObj(words) {
      var o = {};
      for (var i = 0, e = words.length; i < e; ++i) o[words[i]] = true;
      return o;
    }
    var keywords = wordObj([
      'note',
      'across',
      'when',
      'variant',
      'until',
      'unique',
      'undefine',
      'then',
      'strip',
      'select',
      'retry',
      'rescue',
      'require',
      'rename',
      'reference',
      'redefine',
      'prefix',
      'once',
      'old',
      'obsolete',
      'loop',
      'local',
      'like',
      'is',
      'inspect',
      'infix',
      'include',
      'if',
      'frozen',
      'from',
      'external',
      'export',
      'ensure',
      'end',
      'elseif',
      'else',
      'do',
      'creation',
      'create',
      'check',
      'alias',
      'agent',
      'separate',
      'invariant',
      'inherit',
      'indexing',
      'feature',
      'expanded',
      'deferred',
      'class',
      'Void',
      'True',
      'Result',
      'Precursor',
      'False',
      'Current',
      'create',
      'attached',
      'detachable',
      'as',
      'and',
      'implies',
      'not',
      'or',
    ]);
    var operators = wordObj([':=', 'and then', 'and', 'or', '<<', '>>']);

    function chain(newtok, stream, state) {
      state.tokenize.push(newtok);
      return newtok(stream, state);
    }

    function tokenBase(stream, state) {
      if (stream.eatSpace()) return null;
      var ch = stream.next();
      if (ch == '"' || ch == "'") {
        return chain(readQuoted(ch, 'string'), stream, state);
      } else if (ch == '-' && stream.eat('-')) {
        stream.skipToEnd();
        return 'comment';
      } else if (ch == ':' && stream.eat('=')) {
        return 'operator';
      } else if (/[0-9]/.test(ch)) {
        stream.eatWhile(/[xXbBCc0-9\.]/);
        stream.eat(/[\?\!]/);
        return 'ident';
      } else if (/[a-zA-Z_0-9]/.test(ch)) {
        stream.eatWhile(/[a-zA-Z_0-9]/);
        stream.eat(/[\?\!]/);
        return 'ident';
      } else if (/[=+\-\/*^%<>~]/.test(ch)) {
        stream.eatWhile(/[=+\-\/*^%<>~]/);
        return 'operator';
      } else {
        return null;
      }
    }

    function readQuoted(quote, style, unescaped) {
      return function(stream, state) {
        var escaped = false,
          ch;
        while ((ch = stream.next()) != null) {
          if (ch == quote && (unescaped || !escaped)) {
            state.tokenize.pop();
            break;
          }
          escaped = !escaped && ch == '%';
        }
        return style;
      };
    }

    return {
      startState: function() {
        return { tokenize: [tokenBase] };
      },

      token: function(stream, state) {
        var style = state.tokenize[state.tokenize.length - 1](stream, state);
        if (style == 'ident') {
          var word = stream.current();
          style = keywords.propertyIsEnumerable(stream.current())
            ? 'keyword'
            : operators.propertyIsEnumerable(stream.current())
            ? 'operator'
            : /^[A-Z][A-Z_0-9]*$/g.test(word)
            ? 'tag'
            : /^0[bB][0-1]+$/g.test(word)
            ? 'number'
            : /^0[cC][0-7]+$/g.test(word)
            ? 'number'
            : /^0[xX][a-fA-F0-9]+$/g.test(word)
            ? 'number'
            : /^([0-9]+\.[0-9]*)|([0-9]*\.[0-9]+)$/g.test(word)
            ? 'number'
            : /^[0-9]+$/g.test(word)
            ? 'number'
            : 'variable';
        }
        return style;
      },
      lineComment: '--',
    };
  });

  CodeMirror.defineMIME('text/x-eiffel', 'eiffel');
});
